/**
 * Isolated Validator
 * Provides validation capabilities that don't depend on external file system
 */

const ValidatorManager = require('./validators/validator-manager');
const TestConfigFactory = require('./factories/testing-config-factory');

class IsolatedValidator {
  constructor(options = {}) {
    // 默认启用testMode，简化配置
    this.options = {
      testMode: true,
      strict: false,
      allowExtraFields: true,
      mockSchema: true,
      ...options
    };

    this.manager = ValidatorManager.getInstance();
    this.originalValidatorOptions = new Map();
  }

  /**
   * Validate configuration in isolated environment
   * @param {Object} config - Configuration to validate
   * @param {Object} options - Validation options
   * @returns {Object} Validation result
   */
  validateConfig(config, options = {}) {
    const mergedOptions = { ...this.options, ...options };

    // Configure validators for test mode
    this.configureTestMode();

    try {
      const result = this.manager.validateConfig(config, mergedOptions);
      return this.adaptResultForTest(result);
    } finally {
      // Restore original validator options
      this.restoreOriginalOptions();
    }
  }

  /**
   * Validate provider configuration in isolated environment
   * @param {Object} provider - Provider configuration to validate
   * @param {Object} options - Validation options
   * @returns {Object} Validation result
   */
  validateProvider(provider, options = {}) {
    const mergedOptions = { ...this.options, ...options };

    // Configure validators for test mode
    this.configureTestMode();

    try {
      const result = this.manager.validateProvider(provider, mergedOptions);
      return this.adaptResultForTest(result);
    } finally {
      // Restore original validator options
      this.restoreOriginalOptions();
    }
  }

  /**
   * Configure all validators for test mode
   */
  configureTestMode() {
    const validatorNames = this.manager.getValidatorNames();

    for (const name of validatorNames) {
      const validator = this.manager.getValidator(name);
      if (validator && validator.options) {
        // Store original options
        this.originalValidatorOptions.set(name, { ...validator.options });

        // Configure for test mode
        validator.options.testMode = true;
        validator.options.strict = this.options.strict;
        validator.options.allowExtraFields = this.options.allowExtraFields;
      }
    }
  }

  /**
   * Restore original validator options
   */
  restoreOriginalOptions() {
    for (const [name, originalOptions] of this.originalValidatorOptions) {
      const validator = this.manager.getValidator(name);
      if (validator && validator.options) {
        Object.assign(validator.options, originalOptions);
      }
    }
    this.originalValidatorOptions.clear();
  }

  /**
   * Adapt validation result for test environment
   * @param {Object} result - Original validation result
   * @returns {Object} Test-adapted result
   */
  adaptResultForTest(result) {
    if (result.success) {
      return {
        ...result,
        testMode: true,
        warnings: result.warnings || []
      };
    }

    // Enhance error information for testing
    const adaptedErrors = result.errors.map(error => ({
      ...error,
      suggestion: this.getSuggestion(error),
      testMode: true
    }));

    return {
      ...result,
      errors: adaptedErrors,
      testMode: true
    };
  }

  /**
   * Get suggestion for validation error
   * @param {Object} error - Validation error
   * @returns {string} Suggestion
   */
  getSuggestion(error) {
    const fieldSuggestions = {
      'version': 'Use "1.0.0" or "test" for testing',
      'current_provider_model': 'Ensure the model exists in providers configuration',
      'base': 'Use "https://api.test.com" or "mock://test" for testing',
      'api_key': 'Use "test_api_key" or leave empty for testing',
      'models': 'Ensure models is an array with valid model objects',
      'providers': 'Configure at least one provider with required fields'
    };

    const fieldKey = error.field?.split('.').pop() || error.field;
    return fieldSuggestions[fieldKey] || 'Check the field format and value';
  }

  /**
   * Create validation summary with detailed information
   * @param {Object} result - Validation result
   * @returns {Object} Validation summary
   */
  createValidationSummary(result) {
    return {
      success: result.success,
      testMode: this.options.testMode,
      errors: result.errors || [],
      warnings: result.warnings || [],
      errorCount: result.errors ? result.errors.length : 0,
      warningCount: result.warnings ? result.warnings.length : 0,
      fieldErrors: this.groupErrorsByField(result.errors || []),
      suggestions: this.generateSuggestions(result.errors || []),
      timestamp: new Date().toISOString(),
      validationOptions: { ...this.options }
    };
  }

  /**
   * Group errors by field for better debugging
   * @param {Array} errors - Array of validation errors
   * @returns {Object} Errors grouped by field
   */
  groupErrorsByField(errors) {
    const grouped = {};
    errors.forEach(error => {
      const field = error.field || 'unknown';
      if (!grouped[field]) {
        grouped[field] = [];
      }
      grouped[field].push({
        message: error.message,
        suggestion: this.getSuggestion(error),
        value: error.value
      });
    });
    return grouped;
  }

  /**
   * Generate suggestions for all errors
   * @param {Array} errors - Array of validation errors
   * @returns {Array} Array of suggestions
   */
  generateSuggestions(errors) {
    return errors.map(error => ({
      field: error.field,
      message: error.message,
      suggestion: this.getSuggestion(error),
      fixExample: this.getFixExample(error)
    }));
  }

  /**
   * Get fix example for specific error
   * @param {Object} error - Validation error
   * @returns {Object} Fix example
   */
  getFixExample(error) {
    const field = error.field;

    if (field === 'current_provider_model') {
      return {
        before: 'current_provider_model: "non-existent-model"',
        after: 'current_provider_model: "test-test-model"',
        description: 'Use a model ID that exists in your providers configuration'
      };
    }

    if (field === 'base') {
      return {
        before: 'base: "invalid-url"',
        after: 'base: "https://api.test.com" or base: "mock://test"',
        description: 'Use a valid URL or mock URL for testing'
      };
    }

    return {
      description: 'Check the field format and value'
    };
  }

  /**
   * Create test configuration and validate it
   * @param {Object} overrides - Configuration overrides
   * @param {Object} options - Validation options
   * @returns {Object} Validation result
   */
  createAndValidateTestConfig(overrides = {}, options = {}) {
    const config = TestConfigFactory.createConfigWithOverrides(overrides);
    return this.validateConfig(config, options);
  }

  /**
   * Validate multiple configurations
   * @param {Array} configs - Array of configurations to validate
   * @param {Object} options - Validation options
   * @returns {Array} Array of validation results
   */
  validateMultipleConfigs(configs, options = {}) {
    const results = [];
    const mergedOptions = { ...this.options, ...options };

    this.configureTestMode();

    try {
      for (let i = 0; i < configs.length; i++) {
        const config = configs[i];
        const context = { index: i, total: configs.length };

        let result;
        if (config.type === 'provider') {
          result = this.manager.validateProvider(config.data, {
            ...mergedOptions,
            context
          });
        } else {
          result = this.manager.validateConfig(config.data, {
            ...mergedOptions,
            context
          });
        }

        results.push({
          ...this.adaptResultForTest(result),
          index: i,
          type: config.type || 'config'
        });
      }
    } finally {
      this.restoreOriginalOptions();
    }

    return results;
  }

  /**
   * Create test scenario runner
   * @returns {Object} Test scenario runner
   */
  createTestScenarioRunner() {
    return {
      minimalConfig: () => this.createAndValidateTestConfig(
        TestConfigFactory.createMinimalConfig()
      ),
      fullConfig: () => this.createAndValidateTestConfig(
        TestConfigFactory.createFullConfig()
      ),
      invalidConfig: (errorType) => this.validateConfig(
        TestConfigFactory.createInvalidConfig(errorType)
      ),
      multipleProviders: () => this.createAndValidateTestConfig(
        TestConfigFactory.createMultipleProvidersConfig()
      ),
      customConfig: (overrides) => this.createAndValidateTestConfig(overrides)
    };
  }

  /**
   * Validate configuration with mock data
   * @param {Object} mockData - Mock configuration data
   * @param {Object} options - Validation options
   * @returns {Object} Validation result
   */
  validateWithMockData(mockData, options = {}) {
    const enhancedMockData = this.enhanceMockData(mockData);
    return this.validateConfig(enhancedMockData, options);
  }

  /**
   * Enhance mock data with missing required fields
   * @param {Object} mockData - Mock data to enhance
   * @returns {Object} Enhanced mock data
   */
  enhanceMockData(mockData) {
    const baseConfig = TestConfigFactory.createMinimalConfig();

    // Fill in missing required fields
    const enhanced = {
      version: mockData.version || baseConfig.version,
      current_provider_model: mockData.current_provider_model || baseConfig.current_provider_model,
      providers: mockData.providers || baseConfig.providers,
      ...mockData
    };

    return enhanced;
  }

  /**
   * Get validation statistics
   * @param {Object} result - Validation result
   * @returns {Object} Statistics
   */
  getValidationStats(result) {
    return {
      success: result.success,
      errorCount: result.errors ? result.errors.length : 0,
      warningCount: result.warnings ? result.warnings.length : 0,
      fieldCount: this.countFields(result.data || {}),
      hasData: !!result.data,
      testMode: result.testMode || false
    };
  }

  /**
   * Count fields in an object
   * @param {Object} obj - Object to count fields in
   * @returns {number} Field count
   */
  countFields(obj) {
    if (!obj || typeof obj !== 'object') {
      return 0;
    }

    let count = 0;
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        count++;
        if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
          count += this.countFields(obj[key]);
        }
      }
    }
    return count;
  }

  /**
   * Reset validator manager (useful for testing)
   */
  reset() {
    ValidatorManager.resetInstance();
    this.manager = ValidatorManager.getInstance();
    this.originalValidatorOptions.clear();
  }
}

module.exports = IsolatedValidator;